/*
 *  Copyright 2014-2015 snakerflow.com
 *  *
 *  * Licensed under the Apache License, Version 2.0 (the "License");
 *  * you may not use this file except in compliance with the License.
 *  * You may obtain a copy of the License at
 *  *
 *  *     http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS,
 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  * See the License for the specific language governing permissions and
 *  * limitations under the License.
 *
 */
package com.etxxy.web.security.shrio;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.etxxy.common.exception.DataException;
import com.etxxy.common.exception.ServiceException;
import com.etxxy.member.domain.Member;
import com.etxxy.member.service.MemberRoleService;
import com.etxxy.member.service.MemberService;
import com.etxxy.web.constance.WebConstance;

/**
 * shiro的认证授权域
 * 
 * @author yuqs
 * @since 0.1
 */
public class ShiroAuthorizingRealm extends AuthorizingRealm {
	private static Logger logger = LoggerFactory.getLogger(ShiroAuthorizingRealm.class);

	@Autowired
	private MemberService memberService;

	@Autowired
	private MemberRoleService memberRoleService;

	/**
	 * 构造函数，设置安全的初始化信息
	 */
	public ShiroAuthorizingRealm() {
		// super();
		// setAuthenticationTokenClass(UsernamePasswordToken.class);
		// HashedCredentialsMatcher matcher = new
		// HashedCredentialsMatcher(WebConstance.HASH_ALGORITHM);
		// matcher.setHashIterations(WebConstance.HASH_INTERATIONS);
		// setCredentialsMatcher(matcher);
	}

	/**
	 * 获取当前认证实体的授权信息（授权包括：角色、权限）
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		// 获取当前登录的用户名
		ShiroPrincipal subject = (ShiroPrincipal) super.getAvailablePrincipal(principals);
		String username = subject.getUsername();
		String memberId = subject.getMemberId();
		try {
			if (!subject.isAuthorized()) {
				// 根据用户名称，获取该用户所有的权限列表
				List<String> rolelist = memberRoleService.getRoleValuesByMemberId(memberId);
				subject.setRoles(rolelist);
				/*
				 * List<String> authorities =
				 * Member.dao.getAuthoritiesName(userId);
				 * subject.setAuthorities(authorities);
				 */
				subject.setAuthorized(true);
				logger.info("用户【" + username + "】授权初始化成功......");
				logger.info("用户【" + username + "】 角色列表为：" + subject.getRoles());
				logger.info("用户【" + username + "】 权限列表为：" + subject.getAuthorities());
			}
		} catch (RuntimeException e) {
			throw new AuthorizationException("用户【" + username + "】授权失败");
		} catch (ServiceException e) {
			throw new AuthorizationException("用户【" + username + "】授权失败");
		} catch (DataException e) {
			throw new AuthorizationException("用户【" + username + "】授权失败");
		}
		// 给当前用户设置权限
		info.addStringPermissions(subject.getAuthorities());
		info.addRoles(subject.getRoles());
		return info;
	}

	/**
	 * 根据认证方式（如表单）获取用户名称、密码
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		String username = upToken.getUsername();
		if (username == null) {
			logger.warn("用户名不能为空");
			throw new AccountException("用户名不能为空");
		}
		Member member = null;
		try {
			member = memberService.findMemberByMemberName(username);
		} catch (Exception ex) {
			logger.error("获取用户失败ex={}", ex);
		}
		if (member == null) {
			logger.warn("用户不存在");
			throw new UnknownAccountException("用户不存在!");
		}
		if (2 == member.getUserStatus()) {
			logger.warn("用户被禁止使用");
			throw new UnknownAccountException("用户被禁止使用!");
		}
		if (3 == member.getUserStatus()) {
			// 锁定时间
			Date lockedDate = member.getLocktime();
			// 当前时间
			Date nowDate = Calendar.getInstance().getTime();
			// 可以进行解锁时间
			int loginFailureLockTime = WebConstance.CANUNLOCKTIME;
			// 可解锁
			if (isUnLock(lockedDate, nowDate, loginFailureLockTime)) {
				// 进行解锁
				member.setUserStatus(1);
				member.setLoginFailtimes(0);
				try {
					memberService.updateMember(member);
				} catch (ServiceException e) {
					logger.error("调用服务失败e={}", e);
					throw new LockedAccountException("用户被用户被锁定!");
				} catch (DataException e) {
					logger.error("调用数据库失败e={}", e);
					throw new LockedAccountException("用户被用户被锁定!");
				}
			} else {
				logger.error("用户被用户被锁定");
				throw new LockedAccountException("用户被用户被锁定!");
			}

		}
		logger.info("用户【" + username + "】登录成功");
		ShiroPrincipal subject = new ShiroPrincipal(member);
		try {
			//得到权限
			List<String> rolelist = memberRoleService.getRoleValuesByMemberId(member.getId());
			subject.setRoles(rolelist);
		} catch (ServiceException e) {
			logger.error("用户权限服务e={}",e);
		} catch (DataException e) {
			logger.error("用户操作数据库权限e={}",e);
		}
		// List<String> authorities =
		// Member.dao.getAuthoritiesName(user.getStr("id"));
		// List<String> rolelist = Member.dao.getRolesName(user.getStr("id"));
		// subject.setAuthorities(authorities);
		// subject.setRoles(rolelist);
		subject.setAuthorized(true);
		return new SimpleAuthenticationInfo(subject, member.getPassword(), getName());
	}

	/**
	 * @category 是否可以进行解锁
	 * @param lockedDate
	 * @param nowDate
	 * @param loginFailureLockTime
	 * @return
	 */
	private boolean isUnLock(Date lockedDate, Date nowDate, int loginFailureLockTime) {
		// 当锁定时间+解锁时间》当前时间可以进行解锁
		Calendar calendarLock = Calendar.getInstance();
		calendarLock.setTime(lockedDate);
		calendarLock.add(Calendar.DAY_OF_YEAR, loginFailureLockTime);
		Calendar calendarNow = Calendar.getInstance();
		calendarNow.setTime(nowDate);
		if (calendarLock.getTimeInMillis() > calendarNow.getTimeInMillis()) {
			return false;
		} else {
			// 可解锁
			return true;
		}

	}
}
